﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace Cyss.Core.Api.Client
{
    /// <summary>
    /// HttpClientBase 扩展类
    /// </summary>
    public static class HttpClientBaseExtensions
    {
        #region POST

        /// <summary>
        /// POST
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="baseClient"></param>
        /// <param name="action"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        public static async Task<OperateResult<T>> Post<T>(this HttpClientBase baseClient, string action, object model, Dictionary<string, string> Headers = null)
        {
            try
            {
                return await baseClient.SendAsync<OperateResult<T>>(HttpMethod.Post, action, model, Headers);
            }
            catch (Exception ex)
            {
                return ExceptionHandling<T>(ex);
            }
        }

        /// <summary>
        /// POST
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="baseClient"></param>
        /// <param name="model"></param>
        /// <param name="classFilePath"></param>
        /// <param name="memberName"></param>
        /// <returns></returns>
        public static async Task<OperateResult<T>> Post<T>(this HttpClientBase baseClient, object model, Dictionary<string, string> Headers = null, [CallerFilePath] string classFilePath = "", [CallerMemberName] string memberName = "")
        {
            try
            {
                return await baseClient.SendAsync<OperateResult<T>>(HttpMethod.Post, baseClient.GenerateUrl(classFilePath, memberName), model, Headers);
            }
            catch (Exception ex)
            {
                return ExceptionHandling<T>(ex);
            }
        }


        /// <summary>
        /// POST
        /// </summary>
        /// <param name="baseClient"></param>
        /// <param name="action"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        public static async Task<OperateResult> Post(this HttpClientBase baseClient, string action, object model, Dictionary<string, string> Headers = null)
        {
            try
            {
                return await baseClient.SendAsync<OperateResult>(HttpMethod.Post, action, model, Headers);
            }
            catch (Exception ex)
            {
                return ExceptionHandling(ex);
            }
        }

        /// <summary>
        /// POST
        /// </summary>
        /// <param name="baseClient"></param>
        /// <param name="model"></param>
        /// <param name="classFilePath"></param>
        /// <param name="memberName"></param>
        /// <returns></returns>
        public static async Task<OperateResult> Post(this HttpClientBase baseClient, object model, Dictionary<string, string> Headers = null, [CallerFilePath] string classFilePath = "", [CallerMemberName] string memberName = "")
        {
            try
            {
                return await baseClient.SendAsync<OperateResult>(HttpMethod.Post, baseClient.GenerateUrl(classFilePath, memberName), model, Headers);
            }
            catch (Exception ex)
            {
                return ExceptionHandling(ex);
            }
        }

        #endregion


        #region Get

        /// <summary>
        /// Get
        /// </summary>
        /// <param name="baseClient"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public static async Task<OperateResult> Get(this HttpClientBase baseClient, string url, Dictionary<string, string> Headers = null)
        {
            try
            {
                return await baseClient.SendAsync<OperateResult>(HttpMethod.Get, url, Headers);
            }
            catch (Exception ex)
            {
                return ExceptionHandling(ex);
            }
        }

        /// <summary>
        /// Get
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="baseClient"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public static async Task<OperateResult<T>> Get<T>(this HttpClientBase baseClient, string url, Dictionary<string, string> Headers = null)
        {
            try
            {
                return await baseClient.SendAsync<OperateResult<T>>(HttpMethod.Get, url, Headers);
            }
            catch (Exception ex)
            {
                return ExceptionHandling<T>(ex);
            }
        }

        /// <summary>
        /// Get
        /// </summary>
        /// <param name="baseClient"></param>
        /// <param name="urlParameters"></param>
        /// <param name="classFilePath"></param>
        /// <param name="memberName"></param>
        /// <returns></returns>
        public static async Task<OperateResult> Get(this HttpClientBase baseClient, UrlParameters urlParameters = null, Dictionary<string, string> Headers = null, [CallerFilePath] string classFilePath = "", [CallerMemberName] string memberName = "")
        {
            try
            {
                return await baseClient.SendAsync<OperateResult>(HttpMethod.Get, baseClient.GenerateUrl(classFilePath, memberName, urlParameters), Headers);
            }
            catch (Exception ex)
            {
                return ExceptionHandling(ex);
            }
        }

        /// <summary>
        /// Get
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="baseClient"></param>
        /// <param name="urlParameters"></param>
        /// <param name="memberName"></param>
        /// <param name="classFilePath"></param>
        /// <returns></returns>
        public static async Task<OperateResult<T>> Get<T>(this HttpClientBase baseClient, UrlParameters urlParameters = null, Dictionary<string, string> Headers = null, [CallerMemberName] string memberName = "",
        [CallerFilePath] string classFilePath = "")
        {
            try
            {
                var OperateResult = await baseClient.SendAsync<OperateResult<T>>(HttpMethod.Get, baseClient.GenerateUrl(classFilePath, memberName, urlParameters), Headers);
                return OperateResult;
            }
            catch (Exception ex)
            {
                return ExceptionHandling<T>(ex);
            }
        }
        /// <summary>
        /// Get
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="baseClient"></param>
        /// <param name="urlParameters"></param>
        /// <param name="memberName"></param>
        /// <param name="classFilePath"></param>
        /// <returns></returns>
        public static async Task<Stream> GetStream(this HttpClientBase baseClient, UrlParameters urlParameters = null, Dictionary<string, string> Headers = null, [CallerMemberName] string memberName = "",
        [CallerFilePath] string classFilePath = "")
        {
            try
            {
                var OperateResult = await baseClient.GetStreamAsync(HttpMethod.Get, baseClient.GenerateUrl(classFilePath, memberName, urlParameters), Headers);
                return OperateResult;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        #endregion

        #region 文件

        /// <summary>
        /// POST
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="baseClient"></param>
        /// <param name="model"></param>
        /// <param name="classFilePath"></param>
        /// <param name="memberName"></param>
        /// <returns></returns>
        public static async Task<OperateResult<T>> PostFileAsync<T>(this HttpClientBase baseClient, Stream fileStream, string fileName, Dictionary<string, string> Headers = null, [CallerFilePath] string classFilePath = "", [CallerMemberName] string memberName = "")
        {
            try
            {
                return await baseClient.SendFileAsync<OperateResult<T>>(baseClient.GenerateUrl(classFilePath, memberName), fileStream, fileName, Headers);
            }
            catch (Exception ex)
            {
                return ExceptionHandling<T>(ex);
            }
        }


        #endregion

        #region private

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <returns></returns>
        private static async Task<T> SendFileAsync<T>(this HttpClientBase baseClient, string requestUri, Stream fileStream, string fileName, Dictionary<string, string> Headers = null)
        {
            HttpClientMessageModel logModel = new HttpClientMessageModel();
            logModel.ExecuteStartTime = DateTimeOffset.Now;
            try
            {
                var httpClient = baseClient._httpClient;
                HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, requestUri);
                baseClient.SetApiKey(message);
                await baseClient.SetToken(message);
                baseClient.SetMessage(message);
                logModel.Url = httpClient.BaseAddress.AbsoluteUri + requestUri;

                var content = new MultipartFormDataContent();

                //添加文件参数，参数名为files，文件名为123.png
                content.Add(new StreamContent(fileStream), "file", fileName);
                message.Content = content;

                logModel.Content = $"File:{fileName}";
                logModel.Headers = string.Join(",", message.Headers.ToDictionary(x => x.Key, v => string.Join(";", v.Value.ToList())));
                var response = await httpClient.SendAsync(message);
                logModel.IsSuccessStatusCode = response.IsSuccessStatusCode;
                logModel.HttpMethod = HttpMethod.Post.ToString();
                logModel.StatusMessage = response.StatusCode + "/" + response.ReasonPhrase;
                var responseString = await response.Content.ReadAsStringAsync();
                logModel.ResponseString = responseString;
                logModel.ExecuteEndTime = DateTimeOffset.Now;
                if (response.IsSuccessStatusCode)
                {
                    return JsonHelper.DeserializeObject<T>(responseString);
                }
                else
                {
                    throw new Exception($"{response.ReasonPhrase} {responseString}");
                }
            }
            catch (Exception ex)
            {
                logModel.Exception = ex.Message;
                logModel.ExecuteEndTime = DateTimeOffset.Now;
                throw new Exception($"Request Error :url({logModel.Url}) ResponseMessage:" + ex.Message);
            }
            finally
            {
                baseClient.InsertLog(logModel);
            }
        }
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <returns></returns>
        private static async Task<T> SendFileAsync<T>(this HttpClientBase baseClient, string requestUri, string filePath, Dictionary<string, string> Headers = null)
        {
            var fileStream = new FileStream(filePath, FileMode.Open);
            var fileName = Path.GetFileName(filePath);
            return await SendFileAsync<T>(baseClient, requestUri, fileStream, fileName, Headers);
        }

        public static async Task<Stream> GetStreamAsync(this HttpClientBase baseClient, HttpMethod httpMethod, string requestUri, object model = null, Dictionary<string, string> Headers = null)
        {
            HttpClientMessageModel logModel = new HttpClientMessageModel();
            logModel.ExecuteStartTime = DateTimeOffset.Now;
            try
            {
                var httpClient = baseClient._httpClient;
                HttpRequestMessage message = new HttpRequestMessage(httpMethod, requestUri);
                baseClient.SetApiKey(message);
                await baseClient.SetToken(message);
                baseClient.SetMessage(message);
                logModel.Url = httpClient.BaseAddress.AbsoluteUri + requestUri;
                if (Headers != null && Headers.Count() > 0)
                {
                    Headers.ToList().ForEach(header => { message.Headers.Add(header.Key, header.Value); });
                }
                if (model != null)
                {
                    logModel.Content = model.GetType() == typeof(string) ? model : JsonHelper.SerializeObject(model);
                }
                if (model != null)
                {
                    message.Content = new StringContent(JsonHelper.SerializeObject(model), System.Text.Encoding.UTF8, "application/json");
                }
                logModel.Headers = string.Join(",", message.Headers.ToDictionary(x => x.Key, v => string.Join(";", v.Value.ToList())));
                var response = await httpClient.SendAsync(message);
                logModel.IsSuccessStatusCode = response.IsSuccessStatusCode;
                logModel.HttpMethod = HttpMethod.Post.ToString();
                logModel.StatusMessage = response.StatusCode + "/" + response.ReasonPhrase;
                var responseString = await response.Content.ReadAsStreamAsync();
                if (response.IsSuccessStatusCode)
                {
                    return responseString;
                }
                else
                {
                    throw new Exception($"{response.ReasonPhrase} {responseString}");
                }
            }
            catch (Exception ex)
            {
                logModel.Exception = ex.Message;
                logModel.ExecuteEndTime = DateTimeOffset.Now;
                throw new Exception($"Request Error :url({logModel.Url}) ResponseMessage:" + ex.Message);
            }
            finally
            {
                baseClient.InsertLog(logModel);
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="baseClient"></param>
        /// <param name="httpMethod"></param>
        /// <param name="action"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        private static async Task<T> SendAsync<T>(this HttpClientBase baseClient, HttpMethod httpMethod, string requestUri, object model = null, Dictionary<string, string> Headers = null)
        {
            HttpClientMessageModel logModel = new HttpClientMessageModel();
            logModel.ExecuteStartTime = DateTimeOffset.Now;
            try
            {
                var httpClient = baseClient._httpClient;
                HttpRequestMessage message = new HttpRequestMessage(httpMethod, requestUri);
                baseClient.SetApiKey(message);
                await baseClient.SetToken(message);
                baseClient.SetMessage(message);
                logModel.Url = httpClient.BaseAddress.AbsoluteUri + requestUri;
                if (Headers != null && Headers.Count() > 0)
                {
                    Headers.ToList().ForEach(header => { message.Headers.Add(header.Key, header.Value); });
                }
                if (model != null)
                {
                    logModel.Content = model.GetType() == typeof(string) ? model : JsonHelper.SerializeObject(model);
                }
                if (model != null)
                {
                    message.Content = new StringContent(JsonHelper.SerializeObject(model), System.Text.Encoding.UTF8, "application/json");
                }
                logModel.Headers = string.Join(",", message.Headers.ToDictionary(x => x.Key, v => string.Join(";", v.Value.ToList())));
                var response = await httpClient.SendAsync(message);
                logModel.IsSuccessStatusCode = response.IsSuccessStatusCode;
                logModel.HttpMethod = HttpMethod.Post.ToString();
                logModel.StatusMessage = response.StatusCode + "/" + response.ReasonPhrase;
                var responseString = await response.Content.ReadAsStringAsync();
                logModel.ResponseString = responseString;
                logModel.ExecuteEndTime = DateTimeOffset.Now;
                if (response.IsSuccessStatusCode)
                {
                    return JsonHelper.DeserializeObject<T>(responseString);
                }
                else
                {
                    throw new Exception($"{response.ReasonPhrase} {responseString}");
                }
            }
            catch (Exception ex)
            {
                logModel.Exception = ex.Message;
                logModel.ExecuteEndTime = DateTimeOffset.Now;
                throw new Exception($"Request Error :url({logModel.Url}) ResponseMessage:" + ex.Message);
            }
            finally
            {
                baseClient.InsertLog(logModel);
            }
        }


        /// <summary>
        /// 生成URL
        /// </summary>
        /// <param name="baseClient"></param>
        /// <param name="ClassFilePath"></param>
        /// <param name="MemberName"></param>
        /// <param name="urlParameters"></param>
        /// <returns></returns>
        private static string GenerateUrl(this HttpClientBase baseClient, string ClassFilePath, string MemberName, UrlParameters urlParameters = null)
        {
            var ClassName = Path.GetFileName(ClassFilePath);
            if (ClassName.Contains("\\"))
            {
                ClassName = ClassName.Split("\\", StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
            }
            ClassName = ClassName.Replace("ApiClient", "").Replace("Client", "").Replace(".cs", "");

            return ClassName + "/" + MemberName + baseClient.GenerateParameters(urlParameters, ClassFilePath, MemberName);
        }

        /// <summary>
        ///生成URL参数
        /// </summary>
        /// <param name="Parameters"></param>
        /// <param name="memberName"></param>
        /// <returns></returns>
        private static string GenerateParameters(this HttpClientBase baseClient, UrlParameters urlParameters, string ClassFilePath, string MemberName)
        {
            if (urlParameters == null)
            {
                return string.Empty;
            }

            StringBuilder stringBuilder = new StringBuilder();
            var method = baseClient.Methods.FirstOrDefault(x => x.Name == MemberName);
            var Parameters = urlParameters._pars.ToList();
            for (int i = 0; i < method.Parameters.Count; i++)
            {
                var value = Parameters[i];
                if (value == null)
                {
                    continue;
                }
                if (urlParameters.UrlParameterType == UrlParameterType.QuestionMark)
                {

                    if (i > 0)
                    {
                        if (urlParameters.UrlParameterType == UrlParameterType.QuestionMark)
                        {
                            stringBuilder.Append("&");
                        }
                    }
                    if (Parameters[i].GetType() == typeof(DateTime))
                    {
                        var date = ((DateTime)Parameters[i]).ISODateTimeString();
                        //date 转成标准带时区的时间格式
                        stringBuilder.Append($"{method.Parameters[i].Name}={date}");
                    }
                    else
                    {
                        stringBuilder.Append($"{method.Parameters[i].Name}={Parameters[i]}");
                    }
                }
                else
                {
                    if (i > 0)
                    {
                        if (urlParameters.UrlParameterType == UrlParameterType.QuestionMark)
                        {
                            stringBuilder.Append("/");
                        }
                    }
                    if (Parameters[i].GetType() == typeof(DateTime))
                    {
                        var date = ((DateTime)Parameters[i]).ISODateTimeString();
                        //date 转成标准带时区的时间格式
                        stringBuilder.Append(date);
                    }
                    else
                    {
                        stringBuilder.Append(Parameters[i]);
                    }
                }
            }
            if (stringBuilder.Length == 0)
            {
                return string.Empty;
            }
            if (urlParameters.UrlParameterType == UrlParameterType.QuestionMark)
            {
                return "?" + stringBuilder.ToString();
            }
            else
            {
                return "/" + stringBuilder.ToString();
            }

        }

        /// <summary>
        /// 异常处理
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        private static OperateResult ExceptionHandling(Exception ex)
        {
            OperateResult a = new OperateResult();
            a.IsSuccess = false;
            a.Message = ex.Message;
            return a;
        }
        /// <summary>
        /// 异常处理
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ex"></param>
        /// <returns></returns>
        private static OperateResult<T> ExceptionHandling<T>(Exception ex)
        {
            OperateResult<T> a = new OperateResult<T>();
            a.IsSuccess = false;
            a.Message = ex.Message;
            return a;
        }

        /// <summary>
        /// 异常处理
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ex"></param>
        /// <returns></returns>
        private static OperateResult<IEnumerable<T>> ExceptionHandlings<T>(Exception ex)
        {
            OperateResult<IEnumerable<T>> a = new OperateResult<IEnumerable<T>>();
            a.IsSuccess = false;
            a.Message = ex.Message;
            return a;
        }
        #endregion
    }
}
